﻿using System;
using System.Collections.Generic;
using System.Xml;
using System.Xml.Schema;

namespace MockingBird.Xml.XIG
{
    using System.IO;
    // Provider for the instance Generator
    using Microsoft.Xml.XMLGen;

    public class SimpleXmlInstanceGenerator : IXmlInstanceGenerator
    {
        private XmlSampleGenerator generator;
        private IGeneratorSettings settings;
        private XmlSchemaSet schemaSet;
        private XmlQualifiedName rootElementName;

        #region "constructors"
        public SimpleXmlInstanceGenerator()
        {
            SchemaValidationErrors = new List<string>();
        }

        public SimpleXmlInstanceGenerator(GeneratorSettings generatorSettings)
            : this()
        {
            this.settings = generatorSettings;
        }

        #endregion

        #region "Properties"
        public List<string> SchemaValidationErrors { get; private set; }

        #endregion

        #region IXmlInstanceGenerator Members

        /// <summary>
        /// Generates an instance for a specific root element, using the supplied schema set.
        /// </summary>
        /// <param name="schemaSet">Schema set containing root element definition and any dependent definitions.</param>
        /// <param name="rootElementQName">QName of the root element for which the instance is needed.</param>
        /// <returns>Instance Document.</returns>
        public XmlDocument GenerateInstance(XmlSchemaSet schemaSet, XmlQualifiedName rootElementQName)
        {
            this.InitGenerator(schemaSet, rootElementQName);

            XmlDocument resultDocument = this.CreateInstanceDocument();

            return resultDocument;

        }

        /// <summary>
        /// Generates an instance for the specified root element using the supplied xml schema.
        /// </summary>
        /// <param name="schema">XmlSchema containing the definition of the root element and any dependencies.</param>
        /// <param name="rootElement">Name of the element for which an instance is required.</param>
        /// <returns>Instance document.</returns>
        public XmlDocument GenerateInstance(XmlSchema schema, XmlQualifiedName rootElement)
        {
            XmlSchemaSet schemaSet = new XmlSchemaSet();
            schemaSet.Add(schema);
            return this.GenerateInstance(schemaSet, rootElement);
        }

        /// <summary>
        /// Generates an instance for the specified root element using the supplied schema file path.
        /// </summary>
        /// <param name="schemaFileUrl">Path to the schema file.</param>
        /// <param name="rootElement">Name of the element for which an instance is required.</param>
        /// <returns>Instance document.</returns>
        public XmlDocument GenerateInstance(string schemaFileUrl, XmlQualifiedName rootElement)
        {
            XmlSchema schema = XmlSchema.Read(new XmlTextReader(schemaFileUrl), this.ValidationCallBack);
            if (this.SchemaValidationErrors.Count > 0)
            {
                throw new InvalidOperationException("Schema contains errors");
            }

            return this.GenerateInstance(schema, rootElement);
        }
        #endregion

        #region "private helper methods"
        /// <summary>
        /// Helper method to collect all validation errors.
        /// </summary>
        /// <param name="sender">Validation Sender.</param>
        /// <param name="args">Validation Arguments.</param>
        private void ValidationCallBack(object sender, ValidationEventArgs args)
        {
            this.SchemaValidationErrors.Add(args.Message);
        }

        /// <summary>
        /// Initialize the generator.
        /// </summary>
        /// <param name="schemaSet">Supplied SchemaSet.</param>
        /// <param name="rootElementQName">Required root element.</param>
        private void InitGenerator(XmlSchemaSet schemaSet, XmlQualifiedName rootElementQName)
        {
            this.generator = new XmlSampleGenerator(schemaSet, rootElementQName);

            // if one was not already passed in, initialize a new settings class
            // the class will return defaults if they were not explicitly set
            if (this.settings == null)
            {
                this.settings = new GeneratorSettings();
            }

            // set the default settings
            this.generator.MaxThreshold = this.settings.MaxThreshold;
            this.generator.ListLength = this.settings.ListLength;
        }

        /// <summary>
        /// Gets the resulting instance document from the generator.
        /// </summary>
        /// <returns>Instance document.</returns>
        private XmlDocument CreateInstanceDocument()
        {

            MemoryStream memStream = new MemoryStream();
            XmlDocument resultDocument = new XmlDocument();
            XmlWriter writer = XmlWriter.Create(memStream);
            try
            {
                this.generator.WriteXml(writer);

                // if the stream length is still zero then an error has occurred
                if (memStream.Length == 0)
                {
                    throw new InvalidOperationException("The Instance generator could not create an instance for the specified element");
                }

                // move the stream back to the beginning
                // memStream.Position = 0;
                memStream.Seek(0, System.IO.SeekOrigin.Begin);

                resultDocument.Load(memStream);

            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                memStream.Close();
                memStream.Dispose();
            }

            // TBD; flush/close stream/
            return resultDocument;
        }

        #endregion
    }
}
